<!--创建时间：2021/10/22 10:22 描述：vxe-table 自己封装为动态的 -->
<!--
可以自定义的插槽
: default -> 属性
: head -> 属性+ 'Header'
: edit -> 属性+ 'Edit'
 校验规则写在每一项里面
 写在editRender里面，使用rules为key就可以了
 editRender.rules
 如果有editRules 且editRules的key和editRender.rules相同,使用editRules
-->
<script>
import eleDistanceUtils from "@/utils/ele-distance-utils";
import typeOfUtils from "@/utils/type-of-utils";
import {deepClone} from "@/utils";
import listTreeUtils from "@/utils/listTree2List";
import tableUtils from "@/utils/table-utils";
import pageKeyCombination from "@/directive/page-key-combination";
import htmlElementUtils from "@/utils/htmlElementUtils";
import {DefaultFormatter} from "./DataType/defaultData";
import {MyVxeUtils} from "./DataType/myVxeUtils";

const defaultEditConfig = {
  trigger: 'click',
  mode: 'row',
  enabled: false,
  showIcon: false,
  showStatus: true
}

const baseToolBar = {
  label: '操作',
  hidden: false,
  width: '',
  minWidth: '',
  align: 'center',
  fixed: false
}

export default {
  name: "MyVxeTable",
  // 定义用到的组件
  components: {},
  // 自定义指令，这里定义局部指令
  directives: {pageKeyCombination},
  // 过滤器
  filters: {},
  // 定义用到的混入，可以重复使用代码
  mixins: [],
  // 属性定义使用对象的形式进行定义
  props: {
    // 默认自适应当前屏高度
    height: {
      type: [String, Number],
      default: 'auto'
    },
    edit: Boolean,
    // 显示loading
    loading: Boolean,
    head: {
      default: () => [],
      type: Array
    },
    data: {
      default: () => [],
      type: Array
    },
    editConfig: {
      default: () => {
      },
      type: Object
    },
    heightOffset: {
      default: 30,
      type: Number
    },
    headerCellClassName: Function,
    otherBottomElement: HTMLElement,  // 用于计算表格位置距离这个元素的高度,如果没有就是计算到可视区底部
    getFixedColEle: Function, // 参数为表格最外层的元素, 返回一个元素
    size: {
      type: String,
      default: 'small'
    },
    toolBar: {
      type: Object,
      default: () => ({
        label: '操作',
        hidden: false,
        width: '',
        minWidth: '',
        align: 'center',
        fixed: false
      })
    },
  },
  // 定义变量
  data() {
    return {
      tableHeight: 500,
      // 强制开启横向虚拟滚动
      forceHorizontalVirtualScrolling: false,
      onlyOneHead: true,
      scrollX: {
        enabled: true
      },
      columns: []
    }
  },
  // 定义计算属性值，加上watch配合，可以读取vuex异步获取的值
  computed: {
    tableSize() {
      return this?.$ELEMENT?.size || this?.size || 'small'
    },
  },
  // 监听属性值的变化
  watch: {
    columns: {
      handler(value, oldValue) {
        this.$refs?.vxeTableRef?.refreshColumn().then(() => {
          const findIndex = value.findIndex(item => item.hasOwnProperty('children'))
          this.scrollX.enabled = findIndex === -1
          this.$refs?.vxeTableRef?.refreshColumn()
        })
      },
      immediate: true,
      deep: true
    },
    head: {
      handler(value, oldValue) {
        this.columns = this._headToColumns(value)
      },
      immediate: true,
      deep: true
    },
    otherBottomElement(val) {
      if (typeOfUtils.isHTMLDocument(val)) {
        this.vxeGridMounted()
      }
    },
    height(val) {
      if (val !== 'auto') {
        this.tableHeight = val
      }
    }
  },
  // 生命周期钩子函数begin
  // 在页面节点挂载完执行
  mounted() {
  },
  // 在页面创建完执行
  created() {
  },
  // 生命周期钩子函数end
  // 定义方法
  methods: {
    _fixedTran(config){
      if (config.hasOwnProperty('fixed')) {
        config.fixed = typeOfUtils.isBoolean(config.fixed) ? (config.fixed ? 'rigth' : undefined) : config.fixed
      }
    },
    pageKeyCombination() {
      if (this.onlyOneHead) {
        return
      }
      this.forceHorizontalVirtualScrolling = !this.forceHorizontalVirtualScrolling
      this.columns = this._headToColumns(this.head)
    },
    vxeGridMounted() {
      if (this.height === 'auto') {
        const tableE = this.$refs.vxeTableRef.$el
        let height = 0
        if (typeOfUtils.isHTMLDocument(this.otherBottomElement)) {
          height = eleDistanceUtils.elementToBottom(this.otherBottomElement, null)
        } else {
          height = eleDistanceUtils.elementToBottom(tableE, null)
        }
        this.tableHeight = height + tableE.offsetHeight - this.heightOffset
      } else {
        this.tableHeight = this.height
      }
    },
    vxeHeaderCellClassName({$rowIndex, column, row, $columnIndex}) {
      let result = ''
      if (column.className) {
        result += column.className + ' '
      }
      if (typeOfUtils.isNotEmpty(this.headerCellClassName) && typeOfUtils.isFunction(this.headerCellClassName)) {
        result += this.headerCellClassName({row, column, rowIndex: $rowIndex, columnIndex: $columnIndex}) + ' '
      }
      return result
    },
    /**
     *
     * @param item
     * @param h
     * @return {{}}
     */
    getSlots(item, h) {
      const slots = {}
      if (this.$scopedSlots.hasOwnProperty(item.field)) {
        slots.default = (props) => {
          return h('div', [
            this.$scopedSlots[item.field]({
              ...props
            })
          ])
        }
      }
      if (this.$scopedSlots.hasOwnProperty(item.field + 'Header')) {
        slots.header = (props) => {
          return h('div', [
            this.$scopedSlots[item.field + 'Header']({
              ...props
            })
          ])
        }
      }
      if (this.$scopedSlots.hasOwnProperty(item.field + 'Edit')) {
        slots.edit = (props) => {
          return h('div', [
            this.$scopedSlots[item.field + 'Edit']({
              ...props
            })
          ])
        }
      }
      if (typeOfUtils.isEmpty(slots)) {
        return
      }
      return slots
    },
    /**
     * 强制开启横向虚拟滚动, 利用vxe单行表头自动开启横向虚拟滚动方式, 把多级表头压平
     * @param list
     * @return {*[]}
     */
    _forceHorizontalVirtualScrollingMethod(list = []) {
      if (this.forceHorizontalVirtualScrolling && !this.onlyOneHead) {
        const element = this.$refs.vxeTableRef.$el
        const thead = element.getElementsByTagName('thead')[0]
        const th = thead.getElementsByTagName('th')[0]
        const fontSize = htmlElementUtils.getStyle(th, 'font-size')
        return tableUtils.tableHeadFlat(list, fontSize)
      }
      return list
    },
    _headToColumns(value) {
      if (typeOfUtils.isEmpty(value)) {
        return []
      }
      if (typeOfUtils.isNotArray(value)) {
        console.error('表头必须是一个数组')
        return []
      }
      this.scrollX.enabled = false
      const findIndex = value.findIndex(item => item.hasOwnProperty('children'))
      if (findIndex !== -1) {
        this.onlyOneHead = false
      }
      let clone = deepClone(value)
      clone = listTreeUtils.filterTree(clone, item => {
        let isHidden
        if (typeOfUtils.isFunction(item.hidden)) {
          isHidden = item.hidden()
        } else {
          isHidden = (item.hidden === true)
        }
        return !isHidden
      })
      clone = listTreeUtils.mapTree(clone, item => {
        item.title = item.label
        item.field = item.prop
        const formatter = item.formatter;
        if (formatter && typeOfUtils.isFunction(formatter)) {
          item.formatter = ({cellValue, row, column}) => {
            return formatter(cellValue, row, column)
          }
        } else {
          item.formatter = DefaultFormatter.get(item)
        }
        item.slots = {}
        if (this.$scopedSlots.hasOwnProperty(item.field)) {
          item.slots.default = (props, h) => {
            const vNode = h('div', [
              this.$scopedSlots[item.field]({
                ...props
              })
            ])
            return [vNode]
          }
        }
        if (typeOfUtils.isEmpty(item.slots)) {
          delete item.slots
        }
        delete item.query
        return item
      })
      return this._forceHorizontalVirtualScrollingMethod(clone)
    },
    /**
     *
     * @param fieldOrColumn {string | ColumnConfig}
     * @return {*}
     */
    scrollToColumn(fieldOrColumn) {
      return new Promise((resolve, reject) => {
        this.$refs?.vxeTableRef?.scrollToColumn(fieldOrColumn)
          .then(res => {
            resolve(res?.$el)
          })
          .catch(err => reject(err))
      })
    },
    /**
     * @return {*|{virtualX: boolean, virtualY: boolean, scrollTop: number, scrollLeft: number}}
     */
    getScroll() {
      return this.$refs.vxeTableRef?.getScroll()
    },
    /**
     *
     * @param scrollLeft {number}
     * @param scrollTop {number}
     * @return {*}
     */
    scrollTo(scrollLeft, scrollTop) {
      return this.$refs.vxeTableRef?.scrollTo(scrollLeft, scrollTop)
    },
    /**
     * 通过列表field名字滚动到列
     * @param field
     * @return {*}
     */
    scrollToColumnByField(field) {
      return new Promise((resolve, reject) => {
        this.scrollToColumn(field)
          .then(res => {
            if (typeOfUtils.isNotEmpty(this.getFixedColEle) && typeOfUtils.isFunction(this.getFixedColEle)) {
              const ele = this.getFixedColEle(res)
              const left = eleDistanceUtils.elementToLeft(ele, res)
              const scrollStatus = this.getScroll()
              this.scrollTo(scrollStatus.scrollLeft - left - ele.offsetWidth, scrollStatus.scrollTop)
                .then(res => {
                  resolve(res)
                }).catch(err => reject(err))
            } else {
              resolve(res)
            }
          }).catch(err => reject(err))
      })
    },
    updateStatus(scope) {
      return this.$refs.vxeTableRef.updateStatus(scope)
    },
    clearValidate() {
      return this.$refs.vxeTableRef.clearValidate()
    },
    validate(rows) {
      return this.$refs.vxeTableRef.validate(rows)
    }
  },
  render(h) {
    // 渲染列
    const columeItem = (item) => {
      this._fixedTran(item)
      return h('vxe-column', {
        props: {
          ...item
        },
        on: {
          ...item
        },
        scopedSlots: this.getSlots(item, h)
      })
    }
    // 渲染分组
    const colgroupItem = (item) => {
      const children = []
      if (typeOfUtils.isNotEmpty(item.children) && typeOfUtils.isArray(item.children)) {
        item.children.forEach(i => {
          if (i.hasOwnProperty('children')) {
            children.push(colgroupItem(i))
          } else {
            children.push(columeItem(i))
          }
        })
      }
      this._fixedTran(item)
      return h('vxe-colgroup', {
        props: {
          ...item
        },
        on: {
          ...item
        },
        scopedSlots: this.getSlots(item, h)
      }, children)
    }
    const columnList = []
    this.columns.forEach(i => {
      if (i.hasOwnProperty('children')) {
        columnList.push(colgroupItem(i))
      } else {
        columnList.push(columeItem(i))
      }
    })
    // 渲染操作按钮
    if (this.$scopedSlots?.toolbar) {
      const config = Object.assign({}, baseToolBar, this.toolBar)
      if (!config.hidden) {
        this._fixedTran(config)
        columnList.push(h(
          'vxe-column', {
            props: {
              ...config,
              title: config.label
            },
            scopedSlots: {
              default: (props) => {
                return h('div', [
                  this.$scopedSlots.toolbar({
                    ...props
                  })
                ])
              }
            }
          }
        ))
      }
    }
    // 渲染vxeTable
    const vxeTableE = h('vxe-table', {
      ref: "vxeTableRef",
      props: {
        showOverflow: true,
        border: true,
        'highlight-hover-row': true,
        height: this.tableHeight,
        data: this.data,
        size: this.tableSize,
        'keep-source': true,
        ...this.$attrs,
        'edit-config': Object.assign(defaultEditConfig, this.editConfig, {
          enabled: this.edit,
          showIcon: this.edit,
          autoClear: this.editConfig?.autoClearActive || true
        }),
        'scroll-x': Object.assign({}, this.scrollX, this.$attrs['scroll-x']),
        editRules: MyVxeUtils.vxeTableRulesToElementRules(Object.assign(tableUtils.getEditDataTableEditRules(this.head), this.$attrs.editRules)),
      },
      directives: [
        {
          name: 'loading',
          value: this.loading
        },
        {
          name: 'page-key-combination',
          value: this.pageKeyCombination,
          expression: true
        }
      ],
      slot: {
        empty: () => {
          if (this.$slots.empty) {
            return h('div', [
              this.$slots.empty()
            ])
          } else {
            return h('div', {
              directives: [
                {
                  name: 'if',
                  value: !this.loading
                }
              ],
              domProps: {
                innerHTML: '暂无数据'
              },
            })
          }
        }
      },
      class: {
        "vxe-table-table-class": true
      },
      on: {
        ...this.$listeners,
        'hook:mounted': this.vxeGridMounted
      }
    }, columnList)
    return vxeTableE
  }
}
</script>
<!--本地样式，将嵌套深度限制在3级。对于超过4级的嵌套，给予重新评估。这可以避免出现过于详实的CSS选择器。 避免大量的嵌套规则。当可读性受到影响时，将之打断。推荐避免出现多于20行的嵌套规则出现-->
<style scoped lang="scss">
@import "~@/styles/element-variables.scss";

$head-bg-color: var(--table-head-bg-color, '#f2f2f2');
$head-color: $--color-text-regular;
$body-color: $--color-text-regular;
$line-height: 48px;
.vxe-table-table-class {
  ::v-deep {
    thead {
      color: $head-color;

      tr > th {
        font-weight: 600;
        background-color: $head-bg-color;
        font-size: 12px;
      }
    }
  }
}
</style>
